use alloc::string::ToString;
use alloc::{string::String, vec::Vec};
use core::arch::{asm, global_asm};

global_asm!(include_str!("./homework.s"));

// 5-1 在给定一个一组数中查找最大的数
unsafe extern "C" {
    pub fn homework_5_1(a: &[usize]) -> usize;
}

// 5-2 汇编语言实现一个汇编函数，用于比较两个数的大小，并返回较大的值
// 调用函数如下
unsafe extern "C" {
    fn homework_5_2() -> usize;
}
#[unsafe(no_mangle)]
#[inline(never)]
pub extern "C" fn homework_5_2_wrap(a: usize, b: usize) -> usize {
    // 这里的参数a和b本来就是在寄存器a0和a1中的，但是不使用的话，会被rust编译器优化
    // 所以就要用下面的汇编运行一次
    // 从rust 调用汇编本身就是不可移植的
    unsafe {
        asm!(
            r#"
            mv a0, {0}
            mv a1, {1}
            "#,
            in(reg) a,
            in(reg) b,
        );

        homework_5_2()
    }
}

// 这里实现一个比较大小的函数，然后在汇编中进行调用
#[unsafe(no_mangle)]
#[inline(never)]
extern "C" fn homework_5_3(a: usize, b: usize) -> usize {
    if a >= b { a } else { b }
}

unsafe extern "C" {
    pub fn homework_5_3_call();
}

unsafe extern "C" {
    fn homework_5_4_address_table();
    fn homework_5_4_func_table();
    fn homework_5_4_table_size();
}

/// 5-4: 根据函数的地址查询表
#[unsafe(no_mangle)]
#[inline(never)]
pub fn homework_5_4() {
    // 我们把表的地址变成切片,用到了不同的技术来实现指针到rust的类型,
    // 看看我把汇编语言和rust语言融合的融洽不
    unsafe {
        let len = (homework_5_4_table_size as usize as *const usize).read();
        let addr = homework_5_4_address_table as usize as *const usize;
        let mut func = homework_5_4_func_table as usize as *const u8;
        let addr_table = core::ptr::from_raw_parts::<[usize]>(addr, len).as_ref_unchecked();
        let mut func_table: Vec<String> = Default::default();
        for _ in 0..len {
            let mut l = 0;
            while func.read() != 0 {
                l += 1;
                func = func.offset(1);
            }
            let ret = core::slice::from_raw_parts::<u8>(func.offset(-l), l as usize);
            func = func.offset(1);
            func_table.push(String::from_utf8_lossy(ret).to_string());
        }

        // 打印地址与函数名字的对应
        for (i, v) in addr_table.iter().enumerate() {
            println!("0x{:016x} -> {}", v, &func_table[i]);
        }
    }
}

unsafe extern "C" {
    pub fn homework_5_5_test1(a: usize, b: usize) -> usize;
    pub fn homework_5_5_test2(a: usize, b: usize) -> usize;
}
